package com.fms.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fms.constant.MessageConstant;
import com.fms.dto.JsonRequest;
import com.fms.entity.Attendance;
import com.fms.entity.AttendanceTask;
import com.fms.entity.SysUser;
import com.fms.entity.TaskDept;
import com.fms.exception.ServiceException;
import com.fms.mapper.AttendanceMapper;
import com.fms.mapper.AttendanceTaskMapper;
import com.fms.mapper.SysUserMapper;
import com.fms.mapper.TaskDeptMapper;
import com.fms.quartz.JobDataOfInitAttendance;
import com.fms.quartz.SystemSchedulerService;
import com.fms.service.AttendanceService;
import com.fms.service.AttendanceTaskService;
import com.fms.util.CastUtils;
import com.fms.vo.AttendanceTaskInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalTime;
import java.util.List;
import java.util.Objects;


@Service
@RequiredArgsConstructor
@Slf4j
public class AttendanceTaskServiceImpl extends ServiceImpl<AttendanceTaskMapper, AttendanceTask> implements AttendanceTaskService {
    private final AttendanceTaskMapper attendanceTaskMapper;
    private final SystemSchedulerService systemSchedulerService;
    private final TaskDeptMapper taskDeptMapper;
    private final SysUserMapper sysUserMapper;
    private final AttendanceService attendanceService;
    private final AttendanceMapper attendanceMapper;

    @Override
    @Transactional
    public void createAttendanceTask(JsonRequest.AttendanceTaskRequest attendanceTaskRequest) {
        List<Long> deptIds = attendanceTaskRequest.getDeptIds();
        List<Integer> weekDaysList = attendanceTaskRequest.getWeekDays();
        LocalTime startTime = attendanceTaskRequest.getStartTime();
        String cron = CastUtils.castCron(startTime, weekDaysList);
        //插入任务并关联角色
        AttendanceTask attendanceTask = AttendanceTask.builder()
                .inDeadline(attendanceTaskRequest.getInDeadline())
                .startTime(startTime)
                .outDeadline(attendanceTaskRequest.getOutDeadline())
                .status(1)
                .weekDaysString(CastUtils.castWeeksToString(weekDaysList))
                .latitude(attendanceTaskRequest.getLatitude())
                .longitude(attendanceTaskRequest.getLongitude())
                .build();
        Long taskId;
        if( attendanceTaskMapper.insert(attendanceTask)==0){
            throw new ServiceException("插入任务失败");
        }else{
            taskId=attendanceTask.getId();
            deptIds.forEach(deptId -> {
                if( taskDeptMapper.insert(TaskDept.builder().deptId(deptId).taskId(taskId).build())==0){
                    throw new ServiceException("关联角色失败，插入失败");
                }
            });
        }
        //创建定时器
        if(!systemSchedulerService.addScheduler(JobDataOfInitAttendance.builder().deptIds(deptIds).taskId(taskId).build(),
                MessageConstant.TASK_NAME + taskId,
                MessageConstant.TASK_GROUP + taskId,
                cron
        )){
            throw new ServiceException(taskId+"创建添加定时器失败");
        }else{
            log.info("创建任务成功");
            systemSchedulerService.getAllJobKeys();
        }
    }

    @Override
    public Page<AttendanceTaskInfoVO> pageListAttendanceTask(JsonRequest.AttendanceTaskPageRequest attendanceTaskPageRequest) {
        //查询任务
        //获取角色以获取TaskId
        Long taskId=null;
        Long deptId = attendanceTaskPageRequest.getDeptId();
        if(deptId !=null){
            taskId=taskDeptMapper.selectOne(new LambdaQueryWrapper<TaskDept>().eq(TaskDept::getDeptId ,deptId)).getTaskId();
        }
        //分页查询
        Page<AttendanceTask> pageInfo=new Page<>(attendanceTaskPageRequest.getPage(),attendanceTaskPageRequest.getPageSize(),true);
        LambdaQueryWrapper<AttendanceTask> lqw=new LambdaQueryWrapper<>();
        lqw.eq(attendanceTaskPageRequest.getStatus()!=null,AttendanceTask::getStatus,attendanceTaskPageRequest.getStatus())
                .eq(taskId!=null,AttendanceTask::getId,taskId)
                .orderByAsc(AttendanceTask::getStartTime);
        Page<AttendanceTask> page = this.page(pageInfo,lqw);
        //查询关联角色并扩充结果返回
        List<AttendanceTaskInfoVO> list = page.getRecords().stream().map(attendanceTask -> {
            List<Integer> weekDays = CastUtils.castStringToWeeks(attendanceTask.getWeekDaysString());
            return AttendanceTaskInfoVO.builder()
                    .id(attendanceTask.getId())
                    .creator(attendanceTask.getCreator())
                    .createDate(attendanceTask.getCreateDate())
                    .updater(attendanceTask.getUpdater())
                    .updateDate(attendanceTask.getUpdateDate())
                    .weekDaysList(weekDays)
                    .deptIds(taskDeptMapper.selectList(new LambdaQueryWrapper<TaskDept>()
                                    .eq(TaskDept::getTaskId, attendanceTask.getId()))
                            .stream()
                            .map(TaskDept::getDeptId)
                            .toList())
                    .inDeadline(attendanceTask.getInDeadline())
                    .outDeadline(attendanceTask.getOutDeadline())
                    .longitude(attendanceTask.getLongitude())
                    .latitude(attendanceTask.getLatitude())
                    .status(attendanceTask.getStatus())
                    .startTime(attendanceTask.getStartTime())
                    .build();
        }).toList();
        Page<AttendanceTaskInfoVO> realPage=new Page<>(page.getCurrent(),page.getSize());
        realPage.setRecords(list);
        realPage.setTotal(page.getTotal());
        realPage.setPages(page.getPages());
        return realPage;
    }

    @Override
    @Transactional
    public void deleteAttendanceTaskById(Long id) {
        //删除定时器
        if(!systemSchedulerService.deleteScheduler(MessageConstant.TASK_NAME + id, MessageConstant.TASK_GROUP + id)){
            throw new ServiceException("删除定时器失败");
        }
        //删除关联角色
        if(taskDeptMapper.delete(new LambdaQueryWrapper<TaskDept>().eq(TaskDept::getTaskId, id))==0){
            throw new ServiceException("删除关联角色失败");
        }
        //删除任务
        if(attendanceTaskMapper.delete(new LambdaQueryWrapper<AttendanceTask>().eq(AttendanceTask::getId, id))==0){
            throw new ServiceException("删除任务失败");
        }
    }

    @Override
    @Transactional
    public void updateAttendanceTask(Long id, JsonRequest.AttendanceTaskRequest attendanceTaskRequest) {
        //获取角色
        AttendanceTask attendanceTask = attendanceTaskMapper.selectById(id);
        List<Long> deptdiList = taskDeptMapper.
                selectList(new LambdaQueryWrapper<TaskDept>().eq(TaskDept::getTaskId, id))
                .stream()
                .map(TaskDept::getDeptId)
                .toList();

        //对比关键信息
        String cron=null;
        List<Long> updateDeptIds = null;

        List<Integer> weekDays = attendanceTaskRequest.getWeekDays();
        LocalTime startTime = attendanceTaskRequest.getStartTime();

        //判断是否修改角色
        if(!deptdiList.equals(attendanceTaskRequest.getDeptIds())){
            updateDeptIds = attendanceTaskRequest.getDeptIds();
            //删除旧的关联表
            deptdiList.forEach(deptId -> {
                LambdaQueryWrapper<TaskDept> taskDeptLambdaQueryWrapper = new LambdaQueryWrapper<>();
                taskDeptMapper.delete(taskDeptLambdaQueryWrapper.eq(TaskDept::getDeptId, deptId));
            });
            //新增新的关联表
            updateDeptIds.forEach(deptId -> {
                TaskDept taskDept = TaskDept.builder().deptId(deptId).taskId(id).build();
                taskDeptMapper.insert(taskDept);
            });
        }
        //修改定时器
        if(!attendanceTask.getStartTime().equals(startTime) ||!CastUtils.castStringToWeeks(attendanceTask.getWeekDaysString()).equals(weekDays)){
            cron=CastUtils.castCron(startTime,weekDays);
        }
        if(!systemSchedulerService.updateScheduler(MessageConstant.TASK_NAME + id
                , MessageConstant.TASK_GROUP + id
                ,cron
                ,JobDataOfInitAttendance.builder()
                        .deptIds(updateDeptIds)
                        .build())){
            throw new ServiceException("更新任务定时器失败");
        }
        //修改任务表
        if(!this.lambdaUpdate()
                .set(attendanceTaskRequest.getInDeadline()!=null
                        , AttendanceTask::getInDeadline
                        , attendanceTaskRequest.getInDeadline())
                .set(attendanceTaskRequest.getOutDeadline()!=null
                        , AttendanceTask::getOutDeadline
                        , attendanceTaskRequest.getOutDeadline())
                .set(attendanceTaskRequest.getLongitude()!=null
                        , AttendanceTask::getLongitude
                        , attendanceTaskRequest.getLongitude())
                .set(attendanceTaskRequest.getLatitude()!=null
                        , AttendanceTask::getLatitude
                        , attendanceTaskRequest.getLatitude())
                .set(AttendanceTask::getWeekDaysString
                        , CastUtils.castWeeksToString(Objects.requireNonNull(attendanceTaskRequest.getWeekDays())))
                .set(attendanceTaskRequest.getStartTime()!=null,
                        AttendanceTask::getStartTime,
                        startTime)
                .eq(AttendanceTask::getId, id)
                .update()){
            throw new ServiceException("更新任务失败");
        }
    }

    @Override
    @Transactional
    public void disableAttendanceTask(Long id) {
        //冻结计时器
        if(!systemSchedulerService.pauseScheduler(MessageConstant.TASK_NAME + id, MessageConstant.TASK_GROUP + id)){
            throw new ServiceException("冻结定时器失败");
        }
        //修改任务状态
        if(!this.lambdaUpdate().set(AttendanceTask::getStatus, 0 ).eq(AttendanceTask::getId, id).update()){
            throw new ServiceException("禁用任务失败");
        }
    }

    @Override
    @Transactional
    public void enableAttendanceTask(Long id) {
        //恢复计时器
        if(!systemSchedulerService.resumeScheduler(MessageConstant.TASK_NAME + id, MessageConstant.TASK_GROUP + id)){
            throw new ServiceException("恢复定时器失败");
        }
        //修改任务状态
        if(!this.lambdaUpdate().set(AttendanceTask::getStatus, 1 ).eq(AttendanceTask::getId, id).update()){
            throw new ServiceException("恢复任务失败");
        }
    }

    @Override
    @Transactional
    public void endAttendanceTask(Long id) {
        //查找角色
        List<Long> deptIds = taskDeptMapper.selectList(new LambdaQueryWrapper<TaskDept>()
                .eq(TaskDept::getTaskId, id))
                .stream()
                .map(TaskDept::getDeptId)
                .toList();
        //查找用户并停止之前所有任务
        deptIds.forEach(deptId-> sysUserMapper
                .selectList(new LambdaQueryWrapper<SysUser>().select(SysUser::getUsername).eq(SysUser::getDeptId,deptId))
                .forEach(sysUser->{
                    if(!attendanceMapper.selectList(new LambdaQueryWrapper<Attendance>()
                            .ne(Attendance::getStatus, 5)
                            .eq(Attendance::getUsername, sysUser.getUsername())).isEmpty()){
                        if(!attendanceService.lambdaUpdate()
                                .set(Attendance::getStatus, 5)
                                .eq(Attendance::getUsername, sysUser.getUsername())
                                .update()){
                            throw new ServiceException(sysUser.getUsername()+"停止任务时发生异常");
                        }
                    }else{
                        log.info("{}没有要停止的任务",sysUser.getUsername());
                    }
        }));
        //修改任务状态
        if(!this.lambdaUpdate().set(AttendanceTask::getStatus,1 ).eq(AttendanceTask::getId, id).update()){
            throw new ServiceException("结束任务失败");
        }
    }
}
